"""
通知管理API路由
"""
from typing import Any, List, Optional
import json
import asyncio
import logging
from fastapi import APIRouter, Depends, HTTPException, status, Query, Body, Request
from fastapi.responses import StreamingResponse, JSONResponse
from motor.motor_asyncio import AsyncIOMotorDatabase
from bson import ObjectId
from pymongo import ASCENDING, DESCENDING

from app.api.deps import get_db, get_current_user
from app.core.response import success_response, error_response, paginated_response, ErrorTypes, ApiResponse
from app.services.notification_service import NotificationService
from app.services.permission_service import PermissionService
from app.models.notification import NotificationResponse

router = APIRouter()
logger = logging.getLogger(__name__)


@router.get("/", response_model=None, summary="获取通知列表")
async def get_notifications(
    page: int = Query(1, ge=1, description="页码"),
    limit: int = Query(20, ge=1, le=100, description="每页数量"),
    is_read: Optional[bool] = Query(None, description="是否已读筛选"),
    type: Optional[str] = Query(None, description="通知类型筛选"),
    sort_by: str = Query("created_at", description="排序字段"),
    sort_order: str = Query("desc", description="排序方向"),
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    获取通知列表（管理员可查看所有通知）
    """
    try:
        user_id = current_user["user_id"]
        user_roles = current_user.get("roles", [])
        is_admin = "admin" in user_roles or "*" in current_user.get("permissions", [])
        
        # 验证排序参数
        if sort_by not in ["created_at", "is_read"]:
            sort_by = "created_at"
        if sort_order not in ["asc", "desc"]:
            sort_order = "desc"
        
        # 获取通知列表（管理员可以查看所有通知）
        result = await NotificationService.get_user_notifications(
            db=db,
            user_id=user_id,
            page=page,
            limit=limit,
            is_read=is_read,
            notification_type=type,
            sort_by=sort_by,
            sort_order=sort_order,
            is_admin=is_admin
        )
        
        # 使用 paginated_response 返回分页结构
        response = paginated_response(
            items=result["data"],
            total=result["total"],
            page=result["page"],
            size=result["limit"],
            message="获取通知列表成功"
        )
        
        # 使用 JSONResponse 确保 pagination 字段不被过滤
        return JSONResponse(content=response)
    
    except HTTPException:
        raise
    except Exception as e:
        return error_response(
            error_type=ErrorTypes.INTERNAL_ERROR,
            message=f"获取通知列表失败: {str(e)}",
            code=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@router.get("/unread-count", response_model=ApiResponse, summary="获取未读通知数量")
async def get_unread_count(
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    获取未读通知数量（管理员可查看所有未读通知）
    """
    try:
        user_id = current_user["user_id"]
        user_roles = current_user.get("roles", [])
        is_admin = "admin" in user_roles or "*" in current_user.get("permissions", [])
        
        count = await NotificationService.get_unread_count(db=db, user_id=user_id, is_admin=is_admin)
        
        return success_response(
            data={"count": count},
            message="获取未读通知数量成功"
        )
    
    except HTTPException:
        raise
    except Exception as e:
        return error_response(
            error_type=ErrorTypes.INTERNAL_ERROR,
            message=f"获取未读通知数量失败: {str(e)}",
            code=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@router.patch("/{notification_id}/read", response_model=ApiResponse, summary="标记通知为已读")
async def mark_notification_as_read(
    notification_id: str,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    标记单个通知为已读（管理员可以标记任何通知为已读）
    """
    try:
        # 验证ObjectId格式
        try:
            ObjectId(notification_id)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid notification ID format"
            )
        
        user_id = current_user["user_id"]
        user_roles = current_user.get("roles", [])
        is_admin = "admin" in user_roles or "*" in current_user.get("permissions", [])
        
        success = await NotificationService.mark_as_read(
            db=db,
            notification_id=notification_id,
            user_id=user_id,
            is_admin=is_admin
        )
        
        if not success:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Notification not found or access denied"
            )
        
        return success_response(message="标记通知为已读成功")
    
    except HTTPException:
        raise
    except Exception as e:
        return error_response(
            error_type=ErrorTypes.INTERNAL_ERROR,
            message=f"标记通知为已读失败: {str(e)}",
            code=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@router.patch("/read-all", response_model=ApiResponse, summary="标记所有通知为已读")
async def mark_all_notifications_as_read(
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    标记所有通知为已读（管理员可以标记所有通知为已读）
    """
    try:
        user_id = current_user["user_id"]
        user_roles = current_user.get("roles", [])
        is_admin = "admin" in user_roles or "*" in current_user.get("permissions", [])
        
        count = await NotificationService.mark_all_as_read(db=db, user_id=user_id, is_admin=is_admin)
        
        return success_response(
            data={"count": count},
            message=f"已标记 {count} 条通知为已读"
        )
    
    except HTTPException:
        raise
    except Exception as e:
        return error_response(
            error_type=ErrorTypes.INTERNAL_ERROR,
            message=f"标记所有通知为已读失败: {str(e)}",
            code=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@router.delete("/{notification_id}", response_model=ApiResponse, summary="删除通知")
async def delete_notification(
    notification_id: str,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    删除单个通知（管理员可以删除任何通知）
    """
    try:
        # 验证ObjectId格式
        try:
            ObjectId(notification_id)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid notification ID format"
            )
        
        user_id = current_user["user_id"]
        user_roles = current_user.get("roles", [])
        is_admin = "admin" in user_roles or "*" in current_user.get("permissions", [])
        
        success = await NotificationService.delete_notification(
            db=db,
            notification_id=notification_id,
            user_id=user_id,
            is_admin=is_admin
        )
        
        if not success:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Notification not found or access denied"
            )
        
        return success_response(message="删除通知成功")
    
    except HTTPException:
        raise
    except Exception as e:
        return error_response(
            error_type=ErrorTypes.INTERNAL_ERROR,
            message=f"删除通知失败: {str(e)}",
            code=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@router.delete("/", response_model=ApiResponse, summary="批量删除通知")
async def delete_batch_notifications(
    notification_ids: List[str] = Body(..., description="通知ID列表"),
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> Any:
    """
    批量删除通知（管理员可以删除任何通知）
    """
    try:
        # 验证ObjectId格式
        for nid in notification_ids:
            try:
                ObjectId(nid)
            except Exception:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail=f"Invalid notification ID format: {nid}"
                )
        
        user_id = current_user["user_id"]
        user_roles = current_user.get("roles", [])
        is_admin = "admin" in user_roles or "*" in current_user.get("permissions", [])
        
        count = await NotificationService.delete_batch_notifications(
            db=db,
            notification_ids=notification_ids,
            user_id=user_id,
            is_admin=is_admin
        )
        
        return success_response(
            data={"count": count},
            message=f"已删除 {count} 条通知"
        )
    
    except HTTPException:
        raise
    except Exception as e:
        return error_response(
            error_type=ErrorTypes.INTERNAL_ERROR,
            message=f"批量删除通知失败: {str(e)}",
            code=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@router.get("/stream", summary="通知实时推送流")
async def notification_stream(
    request: Request,
    token: Optional[str] = Query(None, description="认证 token（用于 EventSource）"),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> StreamingResponse:
    """
    使用 Server-Sent Events (SSE) 实时推送通知更新
    使用轮询机制检测通知变更
    
    注意：EventSource 不支持自定义 headers，所以通过 token 查询参数传递认证信息
    """
    from app.core.security import verify_token as verify_token_func
    
    # 从查询参数或 header 中获取 token
    auth_token = token
    if not auth_token:
        # 尝试从 header 中获取（如果使用 fetch 等其他方式）
        authorization = request.headers.get("Authorization")
        if authorization and authorization.startswith("Bearer "):
            auth_token = authorization.replace("Bearer ", "")
    
    if not auth_token:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Authentication required: token is missing",
            headers={"WWW-Authenticate": "Bearer"}
        )
    
    # 验证 token
    try:
        payload = verify_token_func(auth_token)
        user_id = payload.get("user_id")
        if not user_id:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid token: missing user_id",
                headers={"WWW-Authenticate": "Bearer"}
            )
        
        # 构建用户信息
        current_user = {
            "user_id": user_id,
            "email": payload.get("email"),
            "roles": payload.get("roles", []),
            "permissions": payload.get("permissions", [])
        }
    except HTTPException as e:
        # 如果是token过期，记录警告日志（token过期是正常情况，不需要记录为错误）
        if "expired" in str(e.detail).lower():
            logger.warning(f"通知流连接失败: Token已过期")
        raise
    except Exception as e:
        error_msg = str(e)
        logger.error(f"Token 验证失败: {error_msg}")
        # 检查是否是token过期
        if "expired" in error_msg.lower():
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Token has expired. Please refresh your token.",
                headers={"WWW-Authenticate": "Bearer"}
            )
        else:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail=f"Invalid token: {error_msg}",
                headers={"WWW-Authenticate": "Bearer"}
            )
    
    user_id = current_user["user_id"]
    user_oid = ObjectId(user_id)
    user_roles = current_user.get("roles", [])
    is_admin = "admin" in user_roles or "*" in current_user.get("permissions", [])
    
    def serialize_notification(notification: NotificationResponse) -> dict:
        """序列化通知为字典"""
        return {
            "id": notification.id,
            "user_id": notification.user_id,
            "type": notification.type,
            "title": notification.title,
            "content": notification.content,
            "related_type": notification.related_type,
            "related_id": notification.related_id,
            "is_read": notification.is_read,
            "read_at": notification.read_at.isoformat() if notification.read_at else None,
            "created_at": notification.created_at.isoformat() if notification.created_at else None,
            "metadata": notification.metadata or {}
        }
    
    async def event_generator():
        """生成 SSE 事件（使用轮询机制）"""
        from datetime import datetime, timezone
        
        try:
            # 初始发送未读数量（管理员可以查看所有未读通知）
            initial_count = await NotificationService.get_unread_count(db=db, user_id=user_id, is_admin=is_admin)
            yield f"data: {json.dumps({'type': 'unread_count', 'count': initial_count})}\n\n"
            
            # 记录上次检查的时间戳
            last_check_time = datetime.now(timezone.utc)
            # 记录已处理的通知ID集合（避免重复推送）
            processed_notification_ids = set()
            
            # 构建查询条件
            query = {}
            if not is_admin:
                query["user_id"] = user_oid
            
            # 心跳计数器（每10次轮询发送一次心跳，减少流量）
            heartbeat_counter = 0
            
            while True:
                try:
                    # 检查客户端是否断开连接（使用更可靠的方法）
                    try:
                        if await request.is_disconnected():
                            logger.info(f"客户端断开连接: {user_id}")
                            break
                    except Exception as disconnect_error:
                        # 如果检查断开连接时出错，可能客户端已断开
                        logger.warning(f"检查连接状态时出错: {disconnect_error}")
                        break
                    
                    # 查询自上次检查以来的新通知
                    new_query = {**query, "created_at": {"$gt": last_check_time}}
                    cursor = db.notifications.find(new_query).sort("created_at", ASCENDING).limit(50)
                    new_notifications = await cursor.to_list(length=50)
                    
                    # 查询自上次检查以来被标记为已读的通知（通过 read_at 字段）
                    updated_query = {**query, "read_at": {"$gt": last_check_time}}
                    cursor = db.notifications.find(updated_query).sort("read_at", ASCENDING).limit(50)
                    updated_notifications = await cursor.to_list(length=50)
                    
                    # 查询自上次检查以来被删除的通知
                    deleted_query = {**query, "updated_at": {"$gt": last_check_time}, "is_deleted": True}
                    cursor = db.notifications.find(deleted_query).sort("updated_at", ASCENDING).limit(50)
                    deleted_notifications = await cursor.to_list(length=50)
                    
                    # 处理新通知
                    for doc in new_notifications:
                        try:
                            notification_id = str(doc["_id"])
                            if notification_id not in processed_notification_ids:
                                notification = NotificationService._doc_to_response(doc)
                                notification_dict = serialize_notification(notification)
                                yield f"data: {json.dumps({'type': 'new_notification', 'notification': notification_dict})}\n\n"
                                processed_notification_ids.add(notification_id)
                        except Exception as e:
                            logger.error(f"处理新通知失败: {str(e)}")
                            continue
                    
                    # 处理更新的通知
                    for doc in updated_notifications:
                        try:
                            notification_id = str(doc["_id"])
                            notification = NotificationService._doc_to_response(doc)
                            notification_dict = serialize_notification(notification)
                            yield f"data: {json.dumps({'type': 'notification_updated', 'notification': notification_dict})}\n\n"
                        except Exception as e:
                            logger.error(f"处理更新通知失败: {str(e)}")
                            continue
                    
                    # 处理删除的通知
                    for doc in deleted_notifications:
                        try:
                            notification_id = str(doc["_id"])
                            yield f"data: {json.dumps({'type': 'notification_deleted', 'notification_id': notification_id})}\n\n"
                        except Exception as e:
                            logger.error(f"处理删除通知失败: {str(e)}")
                            continue
                    
                    # 如果有新通知或更新，更新未读数量
                    if new_notifications or updated_notifications:
                        try:
                            new_count = await NotificationService.get_unread_count(db=db, user_id=user_id, is_admin=is_admin)
                            yield f"data: {json.dumps({'type': 'unread_count', 'count': new_count})}\n\n"
                        except Exception as e:
                            logger.error(f"获取未读数量失败: {str(e)}")
                    
                    # 更新检查时间
                    last_check_time = datetime.now(timezone.utc)
                    
                    # 每10次轮询发送一次心跳（减少流量）
                    heartbeat_counter += 1
                    if heartbeat_counter >= 10:
                        try:
                            yield f"data: {json.dumps({'type': 'heartbeat'})}\n\n"
                            heartbeat_counter = 0
                        except Exception as e:
                            # 如果发送心跳失败，可能客户端已断开
                            logger.warning(f"发送心跳失败: {str(e)}")
                            break
                    
                    # 轮询间隔：3秒
                    await asyncio.sleep(3)
                
                except asyncio.CancelledError:
                    # 任务被取消（客户端断开连接）
                    logger.info(f"流任务被取消: {user_id}")
                    break
                except Exception as e:
                    error_msg = str(e)
                    logger.error(f"轮询通知失败: {error_msg}")
                    # 只发送错误消息，不中断连接
                    try:
                        yield f"data: {json.dumps({'type': 'error', 'message': error_msg})}\n\n"
                    except Exception as send_error:
                        # 如果发送错误消息也失败，可能客户端已断开
                        logger.warning(f"发送错误消息失败: {str(send_error)}")
                        break
                    await asyncio.sleep(3)  # 出错后等待3秒再继续
        
        except asyncio.CancelledError:
            logger.info(f"流生成器被取消: {user_id}")
        except Exception as e:
            error_msg = str(e)
            logger.error(f"通知流连接错误: {error_msg}")
            try:
                yield f"data: {json.dumps({'type': 'error', 'message': error_msg})}\n\n"
            except Exception:
                # 如果发送错误消息失败，忽略
                pass
    
    return StreamingResponse(
        event_generator(),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache, no-transform",
            "Connection": "keep-alive",
            "X-Accel-Buffering": "no",  # 禁用 nginx 缓冲
            "Content-Type": "text/event-stream; charset=utf-8",
            "Access-Control-Allow-Origin": "*",  # 允许跨域（如果需要）
            "Access-Control-Allow-Credentials": "true"
        }
    )

